#include "OgreRenderSystem.h"
#include "OgreGLES2HardwarePixelBuffer.h"
#include "OgreGLES2PixelFormat.h"
#include "OgreGLES2FBORenderTexture.h"
#include "OgreGLES2GpuProgram.h"
#include "OgreRoot.h"

static int computeLog(GLuint value)
{
	int i;
	i = 0;

	if (value == 0) return -1;

	for (;;)
	{
		if (value & 1)
		{
			if (value != 1) return -1;
			return i;
		}
		value = value >> 1;
		i++;
	}
}

namespace Ogre
{
	GLES2HardwarePixelBuffer::GLES2HardwarePixelBuffer(size_t mWidth, size_t mHeight,
													size_t mDepth, PixelFormat mFormat,
													HardwareBuffer::Usage usage)
		: HardwarePixelBuffer(mWidth, mHeight, mDepth, mFormat, usage, false, false),
		  mBuffer(mWidth, mHeight, mDepth, Format),
		  mGLInternalFormat(GL_NONE)
	{
	}

	GLES2HardwarePixelBuffer::~GLES2HardwarePixelBuffer()
	{
		OGRE_DELETE[] (uint8*)mBuffer.data;
	}

	void GLES2HardwarePixelBuffer::allocateBuffer()
	{
		if (mBuffer.data)
			return;

		mBuffer.data = OGRE_NEW_FIX_FOR_WIN32 uint8[mSizeInBytes];
	}

	void GLES2HardwarePixelBuffer::freeBuffer()
	{
		if (mUsage & HBU_STATIC)
		{
			OGRE_DELETE[] (uint8*)mBuffer.data;
			mBuffer.data = 0;
		}
	}

	PixelBox GLES2HardwarePixelBuffer::lockImpl(const Image::Box lockBox, LockOptions options)
	{
		allocateBuffer();
		if (options != HardwareBuffer::HBL_DISCARD
			&& (mUsage & HardwareBuffer::HBU_WRITE_ONLY) == 0)
		{
			download(mBuffer);
		}
		mCurrentLockOptions = options;
		mLockedBox = lockBox;
		return mBuffer.getSubVolume(lockBox);
	}

	void GLES2HardwarePixelBuffer::unlockImpl(void)
	{
		if (mCurrentLockOptions != HardwareBuffer::HBL_READ_ONLY)
		{
			upload(mCurrentLock, mLockedBox);
		}
		freeBuffer();
	}

	void GLES2HardwarePixelBuffer::blitFromMemory(const PixelBox& src, const Image::Box& dstBox)
	{
		if (!mBuffer.contains(dstBox))
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"Destination box out of range",
						"GLES2HardwarePixelBuffer::blitFromMemory");
		}

		PixelBox scaled;

		if (src.getWidth() != dstBox.getWidth() ||
			src.getHeight() != dstBox.getHeight() ||
			src.getDepth() != dstBox.getDepth())
		{
			allocateBuffer();
			scaled = mBuffer.getSubVolume(dstBox);
			Image::scale(src, scaled, Image::FILTER_BILINEAR);
		}
		else if ((src.format != mFormat) ||
			((GLES2PixelUtil::getGLOriginFormat(src.format) == 0) && (src.format != PF_R8G8B8)))
		{
			allocateBuffer();
			scaled = mBuffer.getSubVolume(dstBox);
			PixelUtil::bulkPixelConversion(src, scaled);

			if (mFormat == PF_A4R4G4B4)
			{
				GLES2PixelUtil::convertToGLFormat(scaled, scaled);
			}
		}
		else
		{
			allocateBuffer();
			scaled = src;

			if (src.format == PF_R8G8B8)
			{
				scaled.format = PF_B8G8R8;
				PixelUtil::bulkPixelConversion(src, scaled);
			}
		}

		upload(scaled, dstBox);
		freeBuffer();
	}

	void GLES2HardwarePixelBuffer::blitToMemory(const Image::Box& srcBox, const PixelBox& dst)
	{
		if (!mBuffer.contains(srcBox))
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"source box out of range",
						"GLES2HardwarePixelBuffer::blitToMemory");
		}
		if (srcBox.left == 0 && srcBox.right == getWidth() &&
			srcBox.top == 0 && srcBox.bottom == getHeight() &&
			srcBox.front == 0 && srcBox.back == getDepth() &&
			dst.getWidth() == getWidth() &&
			dst.getHeight() == getHeight() &&
			dst.getDepth() == getDepth() &&
			GLES2PixelUtil::getGLOriginFormat(dst.format) != 0)
		{
			download(dst);
		}
		else
		{
			allocateBuffer();
			download(mbuffer);
			if (srcBox.getWidth() != dst.getWidth() ||
				srcBox.getHeight() != dst.getHeight() ||
				srcBox.getDepth() != dst.getDepth())
			{
				Image::scale(mBuffer.getSubVolume(srcBox), dst, Image::FILTER_BILINEAR);
			}
			else
			{
				PixelUtil::bulkPixelConversion(mBuffer.getSubVolume(srcBox), dst);
			}
			freeBuffer();
		}
	}

	void GLES2HardwarePixelBuffer::upload(const PixelBox& data, const Image::Box& dest)
	{
		OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
			"Upload not possible for this pixelbuffer type",
			"GLES2HardwarePixelBuffer::upload");
	}

	void GLES2HardwarePixelBuffer::download(const PixelBox& data)
	{
		OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
			"Download not possible for this pixelbuffer type",
			"GLES2HardwarePixelBuffer::download");
	}

	void GLES2HardwarePixelBuffer::bindToFramebuffer(GLenum attachment, size_t offset)
	{
		OGRE_EXCEPT(Exception::ERRO_RENDERINGAPI_ERROR,
			"Framebuffer bind not possible for this pixelbuffer type",
			"GLES2HardwarePixelBuffer::bindToFramebuffer");
	}

	GLES2TextureBuffer::GLES2TextureBuffer(const String& baseName, GLenum target, GLuint id,
										GLint width, GLint height, GLint internalFormat, GLint format,
										GLint face, GLint level, Usage usage, bool crappyCard,
										bool writeGamma, uint fsaa)
			: GLES2HardwarePixelBuffer(0, 0, 0, PF_UNKNOWN, usage),
			mTarget(target), mTextureID(id), mFace(face), mLevel(level), mSoftwareMipmap(crappyCard)
	{
		GL_CHECK_ERROR;

		glBindTexture(mTarget, mTextureID);
		GL_CHECK_ERROR;

		mFaceTarget = mTarget;
		if (mTarget == GL_TEXTURE_CUBE_MAP)
			mFaceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;

		mWidth = width;
		mHeight = height;
		mDepth = 1;

		mGLInternalFormat = internalFormat;
		mFormat = GLES2PixelUtil::getClosestOGREFormat(internalFormat, format);

		mRowPitch = mWidth;
		mSlicePitch = mHeight * mWidth;
		mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat);

		mBuffer = PixelBox(mWidth, mHeight, mDepth, mFormat);

		if (mWidth == 0 || mHeight == 0 || mDepth == 0)
			return;

		if (mUsage & TU_RENDERTARGET)
		{
			mSliceTRT.reserve(mDepth);
			for (size_t zoffset = 0; zoffset < mDepth; ++zoffset)
			{
				String name;
				name = "rtt/" + StringConverter::toString((size_t)this) + "/" + baseName;
				GLESSurfaceDesc target;
				target.buffer = this;
				target.zoffset = zoffset;
				RenderTexture* trt = GLES2RTTManager::getSingleton().createRenderTexture(name, target, writeGamma, fsaa);
				mSliceTRT.push_back(trt);
				Root::getSingleton().getRenderSystem()->attachRenderTarget(*mSliceTRT[zoffset]);
			}
		}
	}

	GLES2TextureBuffer::~GLES2TextureBuffer()
	{
		if (mUsage & TU_RENDERTARGET)
		{
			for (SliceTRT::const_iterator it = mSliceTRT.begin(); it != mSliceTRT.end(); ++it)
			{
				Root::getSingleton().getRenderSystem()->destroyRenderTarget((*it)->getName());
			}
		}
	}

	void GLES2TextureBuffer::upload(const PixelBox& data, const Image::Box& dest)
	{
		glBindTexture(mTarget, mTextureID);
		GL_CHECK_ERROR;

		if (PixelUtil::isCompressed(data.format))
		{
			if (data.format != mFormat || !data.isConsecutive())
				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
					"Compressed images must be consecutive, in the source format",
					"GLES2TextureBuffer::upload");

			GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat);

			if (dest.left == 0 && dest.top == 0)
			{
				glCompressedTexImage2D(mFaceTarget, mLevel,
										format,
										dest.getWidth(),
										dest.getHeight(),
										0,
										data.getConsecutiveSize(),
										data.data);
				GL_CHECK_ERROR;
			}
			else
			{
				glCompressedTexImage2D(mFaceTarget, mLevel,
									   dest.left, dest.top,
									   dest.getWidth(), dest.getHeight(),
									   format, data.getConsecutiveSize(),
									   data.data);
				GL_CHECK_ERROR;
			}
		}
		else if (mSoftwareMipmap)
		{
			if (data.getWidth() != data.rowPitch)
			{
				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                            "Unsupported texture format",
                            "GLES2TextureBuffer::upload");
			}
			if (data.getHeight() * data.getWidth() != data.slicePitch)
            {
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                            "Unsupported texture format",
                            "GLES2TextureBuffer::upload");
            }

			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			GL_CHECK_ERROR;
			buildMipmaps(data);
		}
		else
		{
			if (data.getWidth() != data.rowPitch())
			{
				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                            "Unsupported texture format",
                            "GLES2TextureBuffer::upload");
			}

			if (data.getHeight() * data.getWidth() != data.slicePitch)
			{
				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"Unsupported texture format",
						"GLES2TextureBuffer::upload");
			}

			if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3)
			{
				glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
				GL_CHECK_ERROR;
			}

			glTexSubImage2D(mFaceTarget,
							mLevel,
							dest.left, dest.top,
							dest.getWidth(), dest.getHeight(),
							GLES2PixelUtil::getGLOriginFormat(data.format),
							GLES2PixelUtil::getGLOriginDataType(data.format),
							data.data);
			GL_CHECK_ERROR;
		}

		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
		GL_CHECK_ERROR;
	}

	void GLES2TextureBuffer::download(const PixelBox& data)
	{
#if 0 || defined(GL_NV_get_tex_image)
		if (data.getWidth() != getWidth() ||
			data.getHeight() != getHeight() ||
			data.getDepth() != getDepth())
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "only download of entire buffer is supported by GL",
			"GLTextureBuffer::download");
		glBindTexture(mTarget, mTextureID);

		if (PixelUtil::isCompressed(data.format))
		{
			if (data.format != mFormat || !data.isConsecutive())
				OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"Compressed images must be consecutive, in the source format",
				"GLTextureBuffer::download");

			glGetCompressedTexImageNV(mFaceTarget, mLevel, data.data);
		}
		else
		{
			if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3)
			{
				glPixelStorei(GL_PACK_ALIGNMENT, 1);
			}

			glGetTexImageNV(mFaceTarget, mLevel,
				GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format),
				data.data);
			glPixelStorei(GL_PACK_ALIGNMENT, 4);
		}
#else
		OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED,
			"Downloading texture buffers is not supported by OpenGL ES",
			"GLES2TextureBuffer::download");
#endif
	}

	void GLES2TextureBuffer::bindToFrameBuffer(GLenum attachment, size_t zoffset)
	{
		assert(zoffset < mDepth);
		glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
							mFaceTarget, mTextureID, mLevel);
		GL_CHECK_ERROR;
	}

	void GLES2TextureBuffer::copyFromFramebuffer(size_t offset)
	{
		glBindTexture(mTarget, mTextureID);
		GL_CHECK_ERROR;
		glCopyTexSubImage2D(mFaceTarget, mLevel, 0, 0, 0, 0, mWidth, mHeight);
		GL_CHECK_ERROR;
	}

	void GLES2TextureBuffer::blit(const HardwarePixelBufferSharedPtr& src, const Image::Box& srcBox, const Image::Box::dstBox)
	{
		GLES2TextureBuffer* srct = static_cast<GLES2TextureBuffer*>(src.getPointer());

		if ((src->getUsage() & TU_RENDERTARGET) == 0 && (srct->mTarget == GL_TEXTURE_2D))
		{
			blitFromTexture(srct, srcBox, dstBox);
		}
		else
		{
			GLES2HardwarePixelBuffer::blit(src, srcBox, dstBox);
		}
	}

	void GLES2TextureBuffer::blitFromTexture(GLES2TextureBuffer* src, const Image::Box& srcBox, const Image::Box& dstBox)
	{
		GLES2FBOManager* fboMan = static_cast<GLES2FBOManager*>(GLES2RTTManager::getSingletonPtr());

		RenderSystem* rsys = Root::getSingleton().getRenderSystem();
		rsys->_disableTextureUnitsFrom(0);
		glActiveTexture(GL_TEXTURE0);

		glDisable(GL_DEPTH_TEST);
		glDisable(GL_SCISSOR_TEST);
		glDisable(GL_BLEND);
		glDisable(GL_CULL_FACE);

		glBindTexture(src->mTarget, src->mTextureID);
		GL_CHECK_ERROR;

		if (srcBox.getWidth() == dstBox.getWidth() &&
			srcBox.getHeight() == dstBox.getHeight() &&
			srcBox.getDepth() == dstBox.getDepth())
		{
			glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			GL_CHECK_ERROR;
			glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			GL_CHECK_ERROR;
		}
		else
		{
			if (src->mUsage & TU_AUTOMIPMAP)
			{
				glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
				GL_CHECK_ERROR;
				glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				GL_CHECK_ERROR;
			}
			else
			{
				glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
				GL_CHECK_ERROR;
				glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				GL_CHECK_ERROR;
			}
		}
		glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		GL_CHECK_ERROR;
		glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		GL_CHECK_ERROR;

		GLint oldfb;
		glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
		GL_CHECK_ERROR;

		glBindFramebuffer(GL_FRAME_BUFFER, fboMan->getTemporaryFBO());
		GL_CHECK_ERROR;

		GLuint tempTex = 0;
		if (!fboMan->checkFormat(mFormat))
		{
			GLenum tempFormat = GLES2PixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat));
			glGenTextures(1, &tempTex);
			GL_CHECK_ERROR;
			glBindTexture(GL_TEXTURE_2D, tempTex);
			GL_CHECK_ERROR;
#if GL_APPLE_texture_max_level
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, 0);
            GL_CHECK_ERROR;
#endif
			glTexImage2D(GL_TEXTURE_2D, 0, tempFormat,
				GLES2PixelUtil::optionalPO2(dstBox.getWidth()), GLES2PixelUtil::optionalPO2(dstBox.getHeight()),
				0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
			GL_CHECK_ERROR;
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
					GL_TEXTURE_2D, tempTex, 0);
			GL_CHECK_ERROR;
			glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight());
			GL_CHECK_ERROR;
		}
		else
		{
			glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight());
			GL_CHECK_ERROR;
		}

		for (size_t slice = dstBox.front; slice < dstBox.back; ++slice)
		{
			if (!tempTex)
			{
				bindToFramebuffer(GL_COLOR_ATTACHMENT0, slice);
			}

			float u1 = (float)srcBox.left / (float)src->mWidth;
            float v1 = (float)srcBox.top / (float)src->mHeight;
            float u2 = (float)srcBox.right / (float)src->mWidth;
            float v2 = (float)srcBox.bottom / (float)src->mHeight;
			float w = (float)(slice - dstBox.front) / (float)dstBox.getDepth();
			w = w * (float)srcBox.getDepth() + srcBox.front;
			w = (w+0.5f) / (float)src->mDepth;

			glBindTexture(src->mTarget, src->mTextureID);
			GL_CHECK_ERROR;
			glEnable(src->mTarget);
			GL_CHECK_ERROR;

			GLfloat squareVertices[] = {
               -1.0f, -1.0f,
                1.0f, -1.0f,
               -1.0f,  1.0f,
                1.0f,  1.0f,
            };
            GLfloat texCoords[] = {
                u1, v1, w,
                u2, v1, w,
                u2, v2, w,
                u1, v2, w
            };

			glVertexAttribPointer(GLES2GpuProgram::getFixedAttributeIndex(VES_POSITION, 0),
                                  2,
                                  GL_FLOAT,
                                  0,
                                  0,
                                  squareVertices);
            GL_CHECK_ERROR;
            glEnableVertexAttribArray(GLES2GpuProgram::getFixedAttributeIndex(VES_POSITION, 0));
            GL_CHECK_ERROR;
            glVertexAttribPointer(GLES2GpuProgram::getFixedAttributeIndex(VES_TEXTURE_COORDINATES, 0),
                                  3,
                                  GL_FLOAT,
                                  0,
                                  0,
                                  texCoords);
            GL_CHECK_ERROR;
            glEnableVertexAttribArray(GLES2GpuProgram::getFixedAttributeIndex(VES_TEXTURE_COORDINATES, 0));
            GL_CHECK_ERROR;

			glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
			GL_CHECK_ERROR;

			glDisable(src->mTarget);
			GL_CHECK_ERROR;

			if (tempTex)
			{
				glBindTexture(mTarget, mTextureID);
				GL_CHECK_ERROR;
				switch(mTarget)
				{
				case GL_TEXTURE_2D:
				case GL_TEXTURE_CUBE_MAP:
					glCopyTexSubImage2D(mFaceTarget, mLevel,
										dstBox.left, dstBox.top,
										0, 0, dstBox.getWidth(), dstBox.getHeight());
					GL_CHECK_ERROR;
					break;
				}
			}
		}
		if (!tempTex)
		{
			if (mUsage & TU_AUTOMIPMAP)
			{
				glBindTexture(mTarget, mTextureID);
				GL_CHECK_ERROR;
				glGenerateMipmap(mTarget);
				GL_CHECK_ERROR;
			}
		}

		glBindTexture(src->mTarget, src->mTextureID);
		GL_CHECK_ERROR;

		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                     GL_RENDERBUFFER, 0);
		GL_CHECK_ERROR;
		glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
		GL_CHECK_ERROR;
		glDeleteTextures(1, &temptex);
		GL_CHECK_ERROR;
	}

	void GLES2TextureBuffer::blitFromMemory(const PixelBox& src_orig, const Image::Box& dstBox)
	{
		if (PixelUtil::isLuminance(src_orig.format) ||
			PixelUtil::isLuminance(mFormat) ||
			(src_orig.getWidth() == dstBox.getWidth() &&
			src_orig.getHeight() == dstBox.getHeight() &&
			src_orig.getDepth() == dstBox.getDepth()))
		{
			GLES2HardwarePixelBuffer::blitFromMemory(src_orig, dstBox);
			return;
		}
		if (!mBuffer.contains(dstBox))
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range",
			"GLES2TextureBuffer::blitFromMemory");

		MemoryDataStreamPtr buf;
		PixelBox src;

		if (GLES2PixelUtil::getGLOriginFormat(src_orig.format) == 0)
		{
			buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(),
																		src_orig.getDepth(), mFormat)));
			src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr());
			PixelUtil::bulkPixelConversion(src_orig, src);
		}
		else
		{
			src = src_orig;
		}

		GLuint id;
		GLenum target = GL_TEXTURE_2D;
		GLsizei width = GLES2PixelUtil::optionalPO2(src.getWidth());
		GLsizei height = GLES2PixelUtil::optionalPO2(src.getHeight());
		GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(src.format);
		GLenum datatype = GLES2PixelUtil::getGLOriginDataType(src.format);

		glGenTextures(1, &id);
		GL_CHECK_ERROR;

		glBindTexture(target, id);
		GL_CHECK_ERROR;

#if GL_APPLE_texture_max_level
		glTexParameteri(target, GL_TEXTURE_MAX_LEVEL_APPLE, 1000);
		GL_CHECK_ERROR;
#endif

		glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0);
		GL_CHECK_ERROR;

		GLES2TextureBuffer tex(StringUtil::BLANK, target, id, width, height, format, src.format,
                              0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0);

		Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth());
		tex.upload(src, tempTarget);

		blitFromTexture(&tex, &tempTarget, dstBox);

		glDeleteTextures(1, &id);
		GL_CHECK_ERROR;
	}

	RenderTexture* GLES2TextureBuffer::getRenderTarget(size_t zoffset)
	{
		assert(mUsage & TU_RENDERTARGET);
		assert(zoffset < mDepth);
		return mSliceTRT[zoffset];
	}

	void GLES2TextureBuffer::buildMipmaps(const PixelBox& data)
	{
		int width;
		int height;
		int logW;
		int logH;
		int level;
		PixelBox scaled = data;
		scaled.data = data.data;
		scaled.left = data.left;
		scaled.right = data.right;
		scaled.top = data.top;
		scaled.bottom = data.bottom;
		scaled.front = data.front;
		scaled.back = data.back;

		GLenum glFormat = GLES2PixelUtil::getGLOriginFormat(data.format);
        GLenum dataType = GLES2PixelUtil::getGLOriginDataType(data.format);
        width = data.getWidth();
        height = data.getHeight();

		logW = computeLog(width);
		logH = computeLog(height);
		level = (logW > logH ? logW : logH);

		for (int mip = 0; mip <= level; mip++)
		{
			glFormat = GLES2PixelUtil::getGLOriginFormat(scaled.format);
			dataType = GLES2PixelUtil::getGLOriginDataType(scaled.format);

			glTexImage2D(mFaceTarget,
						mip,
						glFormat,
						width, height,
						0,
						glFormat,
						dataType,
						scaled.data);
			GL_CHECK_ERROR;

			if (mip != 0)
			{
				OGRE_DELETE[] (uint8*) scaled.data;
				scaled.data = 0;
			}

			if (width > 1)
			{
				width = width / 2;
			}

			if (height > 1)
			{
				height = height / 2;
			}

			int sizeInBytes = PixelUtil::getMemorySize(width, height, 1, 
														data.format);
			scaled = PixelBox(width, height, 1, data.format);
			scaled.data = OGRE_NEW_FIX_FOR_WIN32 uint8[sizeInBytes];
			Image::scale(data, scaled, Image::FILTER_LINEAR);
		}
	}

	GLES2RenderBuffer::GLES2RenderBuffer(GLenum format, size_t width, size_t height, GLsizei numSamples) :
		GLES2HardwarePixelBuffer(width, height, 1, GLES2PixelUtil::getClosestOGREFormat(format, PF_A8R8G8B8), HBU_WRITE_ONLY)
	{
		mGLInternalFormat = format;

		glGenRenderbuffers(1, &mRenderbufferID);
		GL_CHECK_ERROR;

		glBindRenderbuffer(GL_RENDERBUFFER, mRenderbufferID);
		GL_CHECK_ERROR;

		if (numSamples > 0)
		{
#if GL_APPLE_framebuffer_multisample
			glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER,
												numSamples, format, width, height);
			GL_CHECK_ERROR;
#endif
		}
		else
		{
			glRenderbufferStorage(GL_RENDERBUFFER, format,
									width, height);
			GL_CHECK_ERROR;
		}
	}

	GLES2RenderBuffer::~GLES2RenderBuffer()
	{
		glDeleteRenderbuffers(1, &mRenderbufferID);
		GL_CHECK_ERROR;
	}

	void GLES2RenderBuffer::bindToFramebuffer(GLenum attachment, size_t zoffset)
	{
		assert(zoffset < mDepth);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
								GL_RENDERBUFFER, mRenderbufferID);
		GL_CHECK_ERROR;
	}
};
